#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <chrono>

#include "DateTimeExcep.hpp"

namespace header
{
namespace details
{

// format char
constexpr char HourFormat        = 'h';
constexpr char MinuteFormat      = 'm';
constexpr char SecondFormat      = 's';
constexpr char MicroSecondFormat = 'z';

inline void get_now_time(int& hour, int& minute, int& second, int& microsecond)
{
    auto now = std::chrono::system_clock::now();
    std::time_t now_time_t = std::chrono::system_clock::to_time_t(now);
    std::tm* now_tm = std::localtime(&now_time_t);
    hour = now_tm->tm_hour;
    minute = now_tm->tm_min;
    second = now_tm->tm_sec;
    auto duration = now.time_since_epoch();
    auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(duration).count() % 1000000;
    microsecond = static_cast<int>(microseconds % 1000000);
}

// ============================ parse hour format ========================================
inline void parse_hour_format1(int hour, std::string& res)
{
    res += std::to_string(hour);
}

inline void parse_hour_format2(int hour, std::string& res)
{
    res.push_back(static_cast<char>(hour / 10 + '0'));
    res.push_back(static_cast<char>(hour % 10 + '0'));
}

/**
 * @brief 从 pos 开始解析 format，直到下一字符不是 HourFormat
 * @param pos the start index of parsing format, return the end index of parsing format
 * @param res 解析的结果添加到 res 末尾
 */
inline void parse_hour_format(int& pos, const std::string& format, int hour, std::string& res)
{
    auto len = format.size();
    int cnt = 0;
    for (; pos < len; ++pos) {
        if (format[pos] != details::HourFormat) {
            --pos; 
            break;
        }
        ++cnt;
        if (cnt == 2) {
            parse_hour_format2(hour, res);
            cnt = 0;
        }
    }
    if (cnt == 1) {
        cnt = 0;
        parse_hour_format1(hour, res);
    }
    if (cnt != 0) throw DateTimeExcep{ "illegal time-format: wrong HourFormat count" };
}

// ============================ parse minute format ===============================
inline void parse_minute_format1(int minute, std::string& res)
{
    res += std::to_string(minute);
}

inline void parse_minute_format2(int minute, std::string& res)
{
    res.push_back(static_cast<char>(minute / 10 + '0'));
    res.push_back(static_cast<char>(minute % 10 + '0'));
}

/**
 * @brief 从 pos 开始解析 format，直到下一字符不是 MinuteFormat
 * @param pos the start index of parsing format, return the end index of parsing format
 * @param res 解析的结果添加到 res 末尾
 */
inline void parse_minute_format(int& pos, const std::string& format, int minute, std::string& res)
{
    auto len = format.size();
    int cnt = 0;
    for (; pos < len; ++pos) {
        if (format[pos] != details::MinuteFormat) {
            --pos; 
            break;
        }
        ++cnt;
        if (cnt == 2) {
            parse_minute_format2(minute, res);
            cnt = 0;
        }
    }if (cnt == 1) {
        cnt = 0;
        parse_minute_format1(minute, res);
    }
    if (cnt != 0) throw DateTimeExcep{ "illegal time-format: wrong MinuteFormat count" };
}

// =============================== parse second format ===============================
inline void parse_second_format1(int second, std::string& res)
{
    res += std::to_string(second);
}

inline void parse_second_format2(int second, std::string& res)
{
    res.push_back(static_cast<char>(second / 10 + '0'));
    res.push_back(static_cast<char>(second % 10 + '0'));
}

/**
 * @brief 从 pos 开始解析 format，直到下一字符不是 SecondFormat
 * @param pos the start index of parsing format, return the end index of parsing format
 * @param res 解析的结果添加到 res 末尾
 */
inline void parse_second_format(int& pos, const std::string& format, int second, std::string& res)
{
    auto len = format.size();
    int cnt = 0;
    for (; pos < len; ++pos) {
        if (format[pos] != details::SecondFormat) {
            --pos; 
            break;
        }
        ++cnt;
        if (cnt == 2) {
            parse_second_format2(second, res);
            cnt = 0;
        }
    }
    if (cnt == 1) {
        cnt = 0;
        parse_second_format1(second, res);
    }
    if (cnt != 0) throw DateTimeExcep{ "illegal time-format: wrong SecondFormat count" };
}

// =============================== parse microsecond format ===============================
inline void parse_microsecond_format1(int microsecond, std::string& res)
{
    res += std::to_string(microsecond);
}

/**
 * @brief 从 pos 开始解析 format，直到下一字符不是 MicroSecondFormat
 * @param pos the start index of parsing format, return the end index of parsing format
 * @param res 解析的结果添加到 res 末尾
 */
inline void parse_microsecond_format(int& pos, const std::string& format, int microsecond, std::string& res)
{
    auto len = format.size();
    int cnt = 0;
    for (; pos < len; ++pos) {
        if (format[pos] != details::MicroSecondFormat) {
            --pos; 
            break;
        }
        ++cnt;
        if (cnt == 1) {
            parse_microsecond_format1(microsecond, res);
            cnt = 0;
        }
    }
    if (cnt != 0) throw DateTimeExcep{ "illegal time-format: wrong MicroSecondFormat count" };
}

}   // namespace details

class Time
{
public:
    static void now(int& hour, int& minute, int& second, int& microsecond) 
    { 
        details::get_now_time(hour, minute, second, microsecond);
    }

public:
    Time();
    Time(int h, int m, int s = 0, int ms = 0);
    ~Time() = default;

public:
    int hour() const { return _h; }
    int minute() const { return _m; }
    int second() const { return _s; }
    int microsecond() const { return _ms; }

public:
    // ====================================================
    // [role]: 区分大小写、最长匹配原则
    // 1. hour:
    //      (1)  h:  0 ~ 23
    //      (2) hh: 00 ~ 23
    // 2. minute:
    //      (1)  m:  0 ~ 59
    //      (2) mm: 00 ~ 59
    // 3. second:
    //      (1)  s:  0 ~ 59
    //      (2) ss: 00 ~ 59
    // 4. microsecond:
    //           z: 与 microsecond 相同
    // ====================================================
    std::string to_string(const std::string& format = "hh:mm:ss.z");

private:
    int _h;     // hour
    int _m;     // minute
    int _s;     // second
    int _ms;    // microsecond
};

Time::Time()
{
    now(_h, _m, _s, _ms);
}

Time::Time(int h, int m, int s, int ms)
    :_h{ h }, _m{ m }, _s{ s }, _ms{ ms }
{
    if (_h < 0 || _h > 23) throw DateTimeExcep{ "illegal hour: " + std::to_string(_h) };
    if (_m < 0 || _m > 59) throw DateTimeExcep{ "illegal minute: " + std::to_string(_m) };
    if (_s < 0 || _s > 59) throw DateTimeExcep{ "illegal second: " + std::to_string(_s) };
    if (_ms < 0) throw DateTimeExcep{ "illegal microsecond: " + std::to_string(_ms) };
}

std::string Time::to_string(const std::string& format)
{
    int index = 0;
    auto len = format.size();
    std::string res;
    for (; index < len; ++index) {
        if (format[index] == details::HourFormat) details::parse_hour_format(index, format, _h, res);
        else if (format[index] == details::MinuteFormat) details::parse_minute_format(index, format, _m, res);
        else if (format[index] == details::SecondFormat) details::parse_second_format(index, format, _s, res);
        else if (format[index] == details::MicroSecondFormat) details::parse_microsecond_format(index, format, _ms, res);
        else res.push_back(format[index]);
    }
    return res;
}

}   // namespace header
